﻿using System;
using System.Collections.Generic;
using Think.Problems;
using Think.Problems.Solitaire;

namespace Think.Console
{
    public class SolitaireConsoleRenderer : IConsoleRenderer
    {
        private readonly SolitaireState _state;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="a_state">Solitaire state.</param>
        /// <exception cref="ArgumentNullException">Thrown if "<paramref name="a_state"/>" is null.</exception>
        public SolitaireConsoleRenderer(SolitaireState a_state)
        {
            #region Argument Validation

            if (a_state == null)
                throw new ArgumentNullException("a_state");

            #endregion

            _state = a_state;
        }

        /// <summary>
        /// Render the state to the console at the given coordinates.
        /// </summary>
        /// <param name="x">X coordinate.</param>
        /// <param name="y">Y coordinate.</param>
        public void Render(int x, int y)
        {
            System.Console.SetCursorPosition(x + 0, y + 0);
            RenderFaceDown();
            RenderCard(_state.StockTop);

            RenderFoundation(_state.Foundations[0]);
            RenderFoundation(_state.Foundations[1]);
            RenderFoundation(_state.Foundations[2]);
            RenderFoundation(_state.Foundations[3]);

            var maxy = 0;
            var idx = 0;
            foreach (var tableau in _state.Tableaus)
            {
                var idy = 0;

                for (var i = 0; i < tableau.FaceDownCount; i++)
                {
                    System.Console.SetCursorPosition(x + idx*4, y + 2 + idy);
                    RenderFaceDown();
                    idy++;
                }

                foreach (var card in tableau.FaceUp)
                {
                    System.Console.SetCursorPosition(x + idx * 4, y + 2 + idy);
                    RenderCard(card);
                    idy++;
                }

                idx++;
                maxy = Math.Max(idy, maxy);
            }
        }

        /// <summary>
        /// Dump the current state.
        /// </summary>
        public void Dump()
        {
            Render(0, 5);

            var dumpState = _state.Clone();

            if (dumpState.StockTop != null)
            {
                var idy = 0;
                var first = dumpState.StockTop;

                do
                {
                    System.Console.SetCursorPosition(40, idy++);
                    RenderCard(dumpState.StockTop);

                    dumpState.Draw();

                } while (dumpState.StockTop != first);
            }
        }

        private void RenderFoundation(Foundation a_foundation)
        {
            if (a_foundation.TopCard == null)
                RenderFaceDown();
            else
                RenderCard(a_foundation.TopCard);
        }

        private void RenderFaceDown()
        {
            System.Console.ForegroundColor = ConsoleColor.Gray;
            System.Console.Write("[]  ");
        }

        private void RenderCard(String a_card)
        {
            if (!Card.IsValid(a_card))
            {
                RenderFaceDown();
                return;
            }

            var suits = new Dictionary<CardSuit, String>
            {
                { CardSuit.Spade, "♠" },
                { CardSuit.Heart, "♥" },
                { CardSuit.Diamond, "♦" },
                { CardSuit.Club, "♣" }
            };

            var ranks = new Dictionary<int, String>
            {
                {1, "A"}, {2, "2"}, {3, "3"},
                {4, "4"}, {5, "5"}, {6, "6"},
                {7, "7"}, {8, "8"}, {9, "9"},
                {10, "10"}, {11, "J"}, {12, "Q"},
                {13, "K"},
            };

            var color = Card.Color(a_card);
            var oldColor = System.Console.ForegroundColor;
            System.Console.ForegroundColor = (color == CardColor.Red) ? ConsoleColor.Red : ConsoleColor.White;

            var suit = Card.Suit(a_card);
            var rank = Card.RankValue(a_card);

            var rankString = ranks[rank];
            var suitString = suits[suit];
            if (rankString.Length == 1)
                suitString += " ";

            System.Console.Write("{0}{1} ", rankString, suitString);
        }
    }
}